<?php

/**
 * Celebrio Core
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Core
 */

namespace Celebrio\Core;

use Nette\Object;
use Nette\Environment;
use Nette\Config\ConfigAdapterIni;
use \FileNotFoundException;
use \InvalidArgumentException;
use Celebrio\Core\ConfigException;
use Celebrio\Core\IConfigManagement;

/**
 * Should have been created and used only from Core classes (package visibility)
 * Handles creating the, loading from and saving to configs.
 * 
 * @author pavel
 */
class ConfigManagement extends Object implements IConfigManagement {

    const ON = "on";
    const OFF = "off";

    /**
     * Determines whether the module is installed (it has record in the
     * main ini file - installed.ini).
     *
     * @static
     * @param string $moduleName The module name
     * @return boolean TRUE when the module has been installed (it has line
     *      in the .ini file), FALSE otherwise
     */
    public static function isInstalled($moduleName) {
        $installed = self::getOrCreateConfig("installed");
        return (isset($installed[$moduleName]) && $installed[$moduleName] == self::ON);
    }

    /**
     * @static
     * @throws ConfigException
     * @param string $moduleName
     * @return void
     */
    public static function uninstall($moduleName) {
        if (!self::isInstalled($moduleName)) {
            throw new ConfigException("Trying to uninstall not installed module");
        }

        $installed = static::getOrCreateConfig("installed");
        $installed[$moduleName] = self::OFF;
        static::save($installed, "installed");
    }

    /**
     * @throws ConfigException
     * @param string $moduleName
     * @return void
     */
    public function install($moduleName) {
        if (self::isInstalled($moduleName)) {
            throw new ConfigException("Trying to install already installed module");
        }

        //TODO check the concurrent saving problem (threads)
        $installed = static::getOrCreateConfig("installed");

        $installed[$moduleName] = self::ON;

        // Nette keeps putting php record to the config
        //unset ($installed["php"]);
        static::save($installed, "installed");

    }

    public function saveConfigArray($array, $name) {
        $this->save($array, $name);
    }

    public function loadConfigArray($name) {
        $config = $this->getOrCreateConfig($name);
        return $config;
    }

//
//    /**
//     * automatically saves active config and then the config
//     *
//     * @param activeModule category of module to be set active
//     * @param name name of class to become an active module
//     * @param config array or Config object to save
//     * @param file where to strore configs
//     */
//    public function saveAllConfigs($activeModule, $name, $config, $file) {
//        $this->setActive($activeModule, $name);
//        $this->saveConfig(array ("active" => $this->installed), Environment::getVariable('configDir').'/active.ini');
//        $this->saveConfig($config, $file);
//    }
//
//    /**
//     * set active module (then must be saved!)
//     * use method saveAllConfigs to take care about it
//     * must be private to avoid inconsistency (getActive would return module set active by this method
//     * but it isnt really active until saved)
//     *
//     * TODO: should be more general (not only active but any "category")
//     *
//     * @param module category of modules
//     * @param name name of class to activate
//     */
//    private function setActive($module, $name) {
//        $this->installed[$module] = $name;
//    }


    // --------------- //
    // --- PRIVATE --- //
    // --------------- //


    private static function getOrCreateConfig($name) {
        try {
            $configuration = Environment::loadConfig(static::getFilePath($name));
            $configuration = clone $configuration;
            if ($configuration->offsetExists("php") && $configuration->offsetGet("php") === null) {
                $configuration->offsetUnset("php");
            }
            return $configuration->toArray();
        } catch (FileNotFoundException $e) {
            static::createEmpty($name);
            return array();
        }
    }

    
    /**
     * Creates the EMPTY config file.
     * 
     * @param string $file New config file name.
     */
    private static function createEmpty($file) {
        static::save(array(), $file);
    }

    /**
     * Saves the configuration given as 1st parameter to the file which name
     * is specified in the 2nd parameter. Also creates the file when it does
     * not exist.
     * 
     * @param array $config
     * @param string $file
     */
    private static function save($config, $file) {

        $emptyConfig = array(
            "common" => $config,
            "development < common" => array(),
            "production < development" => array(),
            "console < common" => array(),
        );
        ConfigAdapterIni::save($emptyConfig, static::getFilePath($file));
    }

    /**
     * Creates full path with the config file name. The name must not be empty.
     * 
     * @param string $name Config file name (type name).
     * @return string absolute path
     */
    private static function getFilePath($name) {
        $configDirectoryName = Environment::getVariable('configDir');
        if (empty($name)) {
            throw new InvalidArgumentException("Config name not specified.");
        }
        return $configDirectoryName . "/" . $name . ".ini";
    }

}